//
//  Requestable.swift
//  RequestLib
//
//  Created by meicam on 2020/10/9.
//  Copyright © 2020 刘东旭. All rights reserved.
//

import Foundation

enum HTTPMethod: String {
    case GET
    case POST
}

protocol Requestable {
    var host: String { get }
    var path: String? { get }

    var method: HTTPMethod { get }
    var parameters: [String: Any]? { get }
    var headers: [String: Any]? { get }
    var httpBody: Data? { get }
    
    associatedtype Response: Parsable
    
}

protocol Parsable {
    init(data: Data)
}

extension Parsable {
    static func parse(data: Data) -> Self? {
        return Self.init(data: data) 
    }
}

extension Requestable {
    func request(handler: @escaping (Response?) -> Void) {
        var param = ""
        if method == .GET {
            if parameters?.count ?? 0 > 0 {
                param.append("?")
            }
            parameters?.forEach { (key, value) in
                param.append(key)
                param.append("=")
                param.append("\(value)")
            }
        }
        
        let url = URL(string: host.appending(path ?? "").appending(param.encodeUrl() ?? "" ))!
        var request = URLRequest(url: url)
        request.httpMethod = method.rawValue
        headers?.forEach { (key, value) in
            request.addValue("\(value)", forHTTPHeaderField: key)
        }
        if method == .POST {
            if parameters != nil {
                request.httpBody = try? JSONSerialization.data(withJSONObject: parameters!, options: [])
            }
            if httpBody != nil {
                request.httpBody = httpBody
            }
        }
        let task = URLSession.shared.dataTask(with: request) {
            data, resp, error in
            if let data = data, let res = Response.parse(data: data) {
                DispatchQueue.main.async { handler(res) }
            } else {
                DispatchQueue.main.async { handler(nil) }
            }
        }
        task.resume()
    }
}

extension String {
    func encodeUrl() -> String? {
        return self.addingPercentEncoding( withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
    }
    func decodeUrl() -> String? {
        return self.removingPercentEncoding
    }
}

struct Request<T: Parsable>: Requestable {
    var host: String
    
    var path: String?
    
    var method: HTTPMethod
    
    var parameters: [String : Any]?
    
    var headers: [String : Any]?
    
    var httpBody: Data?
    
    typealias Response = T
}
